// Copyright (C) 2010 Razvan Constantin <razvan.constantin@rconst.net>
//
// This file is part of FileBow.
//
// FileBow is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// FileBow is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FileBow.  If not, see <http://www.gnu.org/licenses/>.

#ifndef FILEBOW_LOGIC_PATH_TREE_H__
#define FILEBOW_LOGIC_PATH_TREE_H__

#include <memory>
#include <string>

namespace Poco {
class Path;
} // namespace Poco

namespace filebow {

/**
 * In memory representation of a file system node (directory/file).
 */
class PathNode {
  // Node type.
  public: enum Type {
    ROOT,
    DIRECTORY,
    FILE,
  };
  // Constructor.
  public: PathNode(Type nodeType, const std::string& nodeName);
  // Destructor.
  public: virtual ~PathNode();
  // Immutable fields.
  public: const Type type;
  public: const std::string name;

  // Navigation.
  public: int size() const { return mSize; }
  public: PathNode* parent() { return mParent; }
  public: const PathNode* parent() const { return mParent; }
  public: PathNode* child() { return mChild.get(); }
  public: const PathNode* child() const { return mChild.get(); }
  public: PathNode* sibling() { return mSibling.get(); }
  public: const PathNode* sibling() const { return mSibling.get(); }
  private: int mSize;
  private: PathNode* mParent;
  private: std::auto_ptr<PathNode> mChild;
  private: std::auto_ptr<PathNode> mSibling;

  // Modifiers.
  protected: PathNode* Insert(PathNode* newChild);
  protected: void Remove();
  friend class PathTree;
  friend class PathNodeTest;
};

inline bool IsDirectory(const PathNode& node) {
  return node.type == PathNode::DIRECTORY;
}
inline bool IsFile(const PathNode& node) {
  return node.type == PathNode::FILE;
}
inline bool IsRoot(const PathNode& node) {
  return node.type == PathNode::ROOT;
}

Poco::Path MakePath(const PathNode& node);
Poco::Path SafeMakePath(const PathNode* node);

/**
 * In memory representation of the file system.
 */
class PathTree {

  // Forward class declarations.
  public: class Node;
  public: class Iterator;
  public: class Walker;

  /** Constructor. */
  public: PathTree();

  /** Destructor. */
  public: ~PathTree();

  /**
   * @return Root node for the path tree. Not a file nor directory. Owned.
   */
  public: Node* root() const { return rootNode; }

  /**
   * @return A walker with noop implementation for current path tree. Owned.
   */
  public: Walker* walker() const { return noopWalker; }

  /**
   * Navigates to the next node in pre-order.
   */
  public: Node* Next(Node* pathNode, Walker* walker = NULL) const;

  /**
   * Inserts a new file or directory into the path tree.
   * @param path The path to insert.
   * @param parentNode Base directory node for the new path. For an absolute
   *        path, the parentNode must be root().
   * @return the node corresponding to inserted path.
   */
  public: Node* Insert(const Poco::Path& path, Node* parentNode);
  public: Node* Insert(const Poco::Path& path) { Insert(path, root()); }

  /**
   * Removes a node and all its descendant from the path tree.
   * The content is destroyed.
   */
  public: void Remove(Node* pathNode);

  /**
   * @param path absolute path to lookup.
   * @return an iterator positioned on the node represented by the given path
   *         or end() if the path is not found.
   */
  public: Node* Find(const Poco::Path& path);

  /**
   * @return The absolute path for a node.
   */
  public: static Poco::Path GetPath(const Node* pathNode);

  /** Root node. Owned. */
  private: Node* rootNode;

  /** Noop walker reference. Not owned. */
  private: Walker* noopWalker;
};

/**
 * Enumeration for path node types.
 */
class PathType {
  public: enum Value {
    NONE = -1,
    ROOT = 0,
    FILE = 1,
    DIR = 2,
    LINK = 3,
  };
};

/**
 * Entry in path tree.
 */
class PathTree::Node {

  /** Constructor accessible only to PathTree. */
  private: Node();

  /** Destructor accessible only to PathTree. */
  private: ~Node();

  /** Parent node or NULL for root. */
  public: Node* parent() const { return parentNode; }

  /** First child or NULL if there are no children. */
  public: Node* firstChild() const { return childNode; }

  /** Next sibling or NULL if there are no more siblings. */
  public: Node* nextSibling() const { return siblingNode; }

  /** Node type: dir, file or root. */
  public: PathType::Value type() const { return nodeType; }

  /** Name. */
  public: const std::string& name() const { return nodeName; }

  /** Type */
  private: PathType::Value nodeType;

  /** Name */
  private: std::string nodeName;

  /** Parent node. Not owned. */
  private: Node* parentNode;

  /** First child node. Owned. */
  private: Node* childNode;

  /** Next sibling. Owned. */
  private: Node* siblingNode;

  // Friendship.
  private: friend class PathTree;
  private: class Helper;
};

/**
 * Iterator over path tree.
 */
class PathTree::Iterator {

  /** Constructor. */
  public: Iterator(PathTree* tree);
  public: Iterator(PathTree* tree, Node* node);
  public: Iterator(PathTree* tree, Walker* node);
  public: Iterator(PathTree* tree, Node* node, Walker* walker);
  private: void Init(PathTree* tree, Node* node, Walker* walker);

  /** Destructor. */
  public: ~Iterator();

  /** Indicates whether the iterator is positioned on a valid node. */
  public: bool done() { return currentNode == NULL; }

  /** Accessors for current node. */
  public: Node* node() { return currentNode; }
  public: Node* operator*() { return node(); }
  public: Node* operator->() { return node(); }
  public: operator Node*() { return node(); }

  /** Next operator. */
  public: void operator++() { Next(); }
  public: void operator++(int) { Next(); }
  public: void Next();

  /** Iterates through all nodes in the path tree. */
  public: void VisitAll();

  /** The tree that owns the iterator. */
  private: PathTree* tree;

  /** Current node. */
  private: Node* currentNode;

  /** Optional walker associated with the iterator. Not owned. */
  private: Walker* walker;
};

/**
 * Abstract visitor declaration which allows custom traversal of a path tree.
 */
class PathTree::Walker {

  /** Constructor. */
  public: Walker();

  /** Destructor. */
  public: virtual ~Walker();

  /**
   * Called when entering in a (sub)directory. Not called for root.
   * @param dirNode Path tree node for (sub)directory being visited.
   */
  public: virtual void EnterDir(const PathTree::Node* dirNode);

  /**
   * Allows skipping the content of current directory at any point.
   * @return true if the current dir should be skipped or false to continue.
   */
  public: virtual bool SkipDir();

  /**
   * Called when going up from current directory.
   * Not called for root.
   */
  public: virtual void ExitDir();

  /**
   * Called when iterating over a new file.
   * @param fileNode Path tree node for file being visited.
   */
  public: virtual void VisitFile(const PathTree::Node* fileNode);

  /**
   * Visits current node.
   */
  private: void VisitNode(const PathTree::Node* node);

  // Friendship
  private: friend class PathTree;
};

bool IsFile(const PathTree::Node& node);
bool IsDir(const PathTree::Node& node);

} // namespace filebow

#endif // FILEBOW_LOGIC_PATH_TREE_H__
